﻿using System;
using System.Collections.Generic;

namespace SIR.Common.Condition
{
	/// <summary>
	/// A predicate defining a single field to value association
	/// </summary>
	public class Predicate : IPredicate
	{
		/// <summary>
		/// Creates a new predicate for the field with the specified operator and value
		/// </summary>
		/// <param name="field">The name of the field</param>
		/// <param name="value">The value for the predicate</param>
		/// <param name="op">The operator for the predicate</param>
		public Predicate(string field, object value, PredicateOperator op)
			: this(field, field, value, op)
		{
		}

		/// <summary>
		/// Creates a new predicate for the field with the specified operator and value
		/// </summary>
		/// <param name="field">The name of the field</param>
		/// <param name="parameterName">The name of the parameter to use</param>
		/// <param name="value">The value for the predicate</param>
		/// <param name="op">The operator for the predicate</param>
		public Predicate(string field, string parameterName, object value, PredicateOperator op)
		{
			_field = field.Replace(".", "].[");
			_parameterName = "@" + System.Text.RegularExpressions.Regex.Replace(parameterName, "[^a-zA-Z0-9]", "");
			_value = value;
			_op = op;
		}

		private string _field = null;
		private object _value = null;
		private string _parameterName = null;
		private PredicateOperator _op;

		/// <summary>
		/// Formats the predicate into a SQL friendly statement
		/// </summary>
		/// <returns>The SQL representation of the predicate</returns>
		public string Format()
		{
			// not handling null values right now
			if (_value == null)
				throw new System.NotImplementedException();
			return string.Format("[{0}] {2} {1}", _field, _parameterName, _op.ToString());
		}

		/// <summary>
		/// Checks to see if the predicate already contains the field
		/// </summary>
		/// <param name="key">The field to check for</param>
		/// <returns>True if the predicate contains the specified field</returns>
		public bool ContainsKey(string key)
		{
			return _field == key || _field.IndexOf(".[" + key) > 0;
		}

		/// <summary>
		/// Parse the string into a predicate object
		/// </summary>
		/// <param name="condition">A string representation of the predicate</param>
		/// <returns>A new predicate object with the correct field, value, and operator</returns>
		internal static Predicate Parse(string condition)
		{
			int endField = -1, startValue = -1;
			PredicateOperator foundOp = null;
			// Loop through all operators
			foreach (var op in PredicateOperator.AllOperators)
			{
				// Check if this is the operator being used
				endField = condition.IndexOf(" " + op.ToString() + " ");

				// If operator was found
				if (endField > -1)
				{
					// Set the operator
					foundOp = op;
					// Find where the value field starts
					startValue = endField + op.ToString().Length + 2;
					break;
				}
			}

			// If operator was found
			if (endField > 0)
			{
				// Return a new predicate
				return new Predicate(condition.Substring(0, endField),
					condition.Substring(startValue), foundOp);
			}

			// Operator not found
			throw new Exception("Invalid condition");
		}

		/// <summary>
		/// Gets the parameters and their values for the predicate
		/// </summary>
		/// <returns>A dictionary containing the paramaeter names and values</returns>
		public Dictionary<string, string> GetParameters()
		{
			return new Dictionary<string, string>() { { _parameterName, _value.ToString() } };
		}
	}
}